package com.example.icampusevent;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import java.util.ArrayList;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import com.example.icampusevent.jsonobjects.TimeQueryEvent;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.view.ScaleGestureDetector;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.view.MotionEvent;
import android.widget.Adapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class CircleOverlay extends Overlay {

	private MainActivity mainActivity;
	private Handler handler;
	
    private double lantitude;
    private double longitude;
    private float radius;
    private MapView mapView;
    private ScaleGestureDetector mScaleDetector;
    private float mScaleFactor = 1.f;

    private float mLastTouchX;
    private float mLastTouchY;

    private Point point = null;
    private Paint innerCirclePaint;
    
    private static final int INVALID_POINTER_ID = -1;
    private int mActivePointerId = INVALID_POINTER_ID;
    private final int UPDATE = 0;

    public CircleOverlay(Activity activity, double _lat, double _lon, float _defaultRadius) {
    	mainActivity = (MainActivity)activity;
        lantitude = _lat;
        longitude = _lon;
        radius = _defaultRadius;
        handler = mainActivity.getHandler();
        
        // Create our ScaleGestureDetector
        mScaleDetector = new ScaleGestureDetector(mainActivity, new ScaleListener());
    }

    @Override
    public void draw(Canvas canvas, MapView mapView, boolean shadow) {
    	this.mapView = mapView;
        super.draw(canvas, mapView, shadow); 

        if(shadow) return; // Ignore the shadow layer
        Projection projection = mapView.getProjection();
        float circleRadius = projection.metersToEquatorPixels(radius);
        if(point == null) {
            point = new Point();
            GeoPoint geo = new GeoPoint((int) (lantitude *1E6), (int)(longitude * 1E6));

            projection.toPixels(geo ,point);
            innerCirclePaint = new Paint();
            innerCirclePaint.setColor(Color.BLUE);
            innerCirclePaint.setAlpha(25);
            innerCirclePaint.setAntiAlias(true);
            innerCirclePaint.setStyle(Paint.Style.FILL);
        }
        canvas.scale(mScaleFactor, mScaleFactor, (float)point.x, (float)point.y);
        canvas.drawCircle((float)point.x, (float)point.y, circleRadius, innerCirclePaint);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event, MapView mapView) {
    	this.mapView = mapView;
    	
    	// Let the ScaleGestureDetector inspect all events.
        mScaleDetector.onTouchEvent(event);
        
    	final int action = event.getAction();
    	switch (action & MotionEvent.ACTION_MASK) {
    		case MotionEvent.ACTION_DOWN: {
    			final float x = event.getX();
    			final float y = event.getY();
    			
    			// Remember where we started
    			mLastTouchX = x;
    			mLastTouchY = y;
    			
    			// Save the ID of this pointer
    	        mActivePointerId = event.getPointerId(0);
    			break;
    		}
    		
    		case MotionEvent.ACTION_MOVE: {
    			// Find the index of the active pointer and fetch its position
    	        final int pointerIndex = event.findPointerIndex(mActivePointerId);
    	        final float x = event.getX(pointerIndex);
    	        final float y = event.getY(pointerIndex);
    	        
    	        // Only move if the ScaleGestureDetector isn't processing a gesture.
    	        if (!mScaleDetector.isInProgress()) {
    	        	// Calculate the distance moved
        	        final float dx = x - mLastTouchX;
        	        final float dy = y - mLastTouchY;
        	        
        	        // Move the object
        	        point.x += dx;
        	        point.y += dy;

        	        // Invalidate to request a redraw
        	        mapView.invalidate();
    	        }
    	        
    	        // Remember this touch position for the next move event
    	        mLastTouchX = x;
    	        mLastTouchY = y;
    	        break;
    	    }
    		case MotionEvent.ACTION_UP: {
    	        mActivePointerId = INVALID_POINTER_ID;
    	        
    	        // Update the event list, since user has changed the range circle
    	        float currentRadius = mScaleFactor * radius;
    	        Projection projection = mapView.getProjection();
    	        GeoPoint currentPoint = projection.fromPixels(point.x, point.y);
    	        String url = "http://hamedaan.usc.edu:8080/team15/GeoServlet?q=range&lat=" + (currentPoint.getLatitudeE6() / 1E6) + 
    	        		"" + "&lng=" + (currentPoint.getLongitudeE6() / 1E6) + "&radius=" + (currentRadius * 0.85);
    	        LoadEventListTask task = new LoadEventListTask();
    	        task.execute(url);
    	        
    	        break;
    	    }
    		
    		case MotionEvent.ACTION_CANCEL: {
    	        mActivePointerId = INVALID_POINTER_ID;
    	        break;
    	    }
    		
    		case MotionEvent.ACTION_POINTER_UP: {
    	        // Extract the index of the pointer that left the touch sensor
    	        final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) 
    	                >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
    	        final int pointerId = event.getPointerId(pointerIndex);
    	        if (pointerId == mActivePointerId) {
    	            // This was our active pointer going up. Choose a new
    	            // active pointer and adjust accordingly.
    	            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
    	            mLastTouchX = event.getX(newPointerIndex);
    	            mLastTouchY = event.getY(newPointerIndex);
    	            mActivePointerId = event.getPointerId(newPointerIndex);
    	        }
    	        break;
    	    }
    	}
    	return true;
    }
    
    private class LoadEventListTask extends AsyncTask<String, Integer, Reader> {
	     protected Reader doInBackground(String... url) {
	    	 InputStream source = retrieveStream(url[0]);
	 		 if(source == null) return null;
	 		 
	 		 Reader inputReader = new InputStreamReader(source);
	 		 return inputReader;
	     }

	     protected void onPostExecute(Reader json) {
	    	 Type listType = new TypeToken<ArrayList<TimeQueryEvent>>(){}.getType();
	 		 Gson gson = new Gson();
	 		 ArrayList<TimeQueryEvent> eventList = gson.fromJson(json, listType);
			 mainActivity.setEventList(eventList);
			 handler.sendEmptyMessage(UPDATE);
	     }
	     
	    // Get data stream from server
	 	private InputStream retrieveStream(String url) {
	 		DefaultHttpClient client = new DefaultHttpClient();
	 		HttpGet request = new HttpGet(url);
	 		try {
	 			HttpResponse response = client.execute(request);
	 			final int status = response.getStatusLine().getStatusCode();
	 			if(status != HttpStatus.SC_OK) {
	 				toast("ERROR CODE: " + status + " for URL " + url);
	 				return null;
	 			}
	 			HttpEntity responseEntity = response.getEntity();
	 			return responseEntity.getContent();
	 		}
	 		catch(IOException e) {
	 			request.abort();
	 			toast("ERROR for " + url);
	 		}
	 		
	 		return null;
	 	}
	}
    
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();
            
            // Don't let the object get too small or too large.
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
            mapView.invalidate();
            return true;
        }
    }
    
    public void toast(String s) {
		CharSequence text = s;
		int duration = Toast.LENGTH_SHORT;
		Toast toast = Toast.makeText(mainActivity, text, duration);
		toast.show();
	}
}